package drds.global_transaction.resource_manager.execute;

import drds.common.$;
import drds.global_transaction.resource_manager.ParameterObjectList;
import drds.global_transaction.resource_manager.model.Image;
import drds.global_transaction.resource_manager.proxy.resource_manager.StatementProxy;
import drds.global_transaction.resource_manager.sql_recognizer.SelectSqlWhereConditionRecognizer;
import drds.global_transaction.resource_manager.sql_recognizer.SqlRecognizer;

import java.sql.*;
import java.util.ArrayList;

public class SelectForUpdateExecutor<S extends Statement> extends AbstractExecutor<ResultSet, S>
{

	public SelectForUpdateExecutor(SqlRecognizer sqlRecognizer, StatementProxy<S> statementProxy, StatementExecuteTemplate<ResultSet, S> statementExecuteTemplate)
	{
		super(sqlRecognizer, statementProxy, statementExecuteTemplate);
	}

	@Override
	public Object doExecute(Object... args) throws Exception
	{
		SelectSqlWhereConditionRecognizer selectSqlWhereConditionRecognizer = (SelectSqlWhereConditionRecognizer) this.sqlRecognizer;

		Connection connection = statementProxy.getConnection();
		ResultSet resultSet = null;
		Savepoint savepoint = null;
		LockRetryController lockRetryController = new LockRetryController();
		boolean originalAutoCommit = connection.getAutoCommit();

		StringBuilder sb = new StringBuilder("select ");
		sb.append(getTableMetaData().getFirstPrimaryKeyColumnName());
		sb.append(" from " + getTableMetaData().getTableName());
		String whereCondition = null;
		ArrayList<Object> objectList = new ArrayList<>();
		if (statementProxy instanceof ParameterObjectList)
		{
			whereCondition = selectSqlWhereConditionRecognizer.getWhereCondition((ParameterObjectList) statementProxy, objectList);
		} else
		{
			whereCondition = selectSqlWhereConditionRecognizer.getWhereCondition();
		}
		if ($.isNotNullAndNotEmpty(whereCondition))
		{
			sb.append(" where " + whereCondition);
		}
		sb.append(" for update");
		String sql = sb.toString();
		try
		{
			if (originalAutoCommit)
			{
				connection.setAutoCommit(false);
			}
			savepoint = connection.setSavepoint();
			resultSet = statementExecuteTemplate.execute(statementProxy.getStatement(), args);
			while (true)
			{
				// Try to get global lock_manager of those rows selected
				Statement statement = null;

				ResultSet resultSet1 = null;
				try
				{
					if (objectList.isEmpty())
					{
						statement = statementProxy.getConnection().createStatement();
						resultSet1 = statement.executeQuery(sql);
					} else
					{
						PreparedStatement preparedStatement = statementProxy.getConnection().prepareStatement(sql);
						statement = preparedStatement;
						for (int i = 0; i < objectList.size(); i++)
						{
							preparedStatement.setObject(i + 1, objectList.get(i));
						}
						resultSet1 = preparedStatement.executeQuery();
					}

					Image image = Image.buildImage(getTableMetaData(), resultSet1);
					statementProxy.getConnectionProxy().lockRecordIdsCheck(image);// 针对image进行锁定
					break;
				} catch (LockConflictException lce)
				{
					connection.rollback(savepoint);
					lockRetryController.sleep(lce);

				} finally
				{
					if (resultSet1 != null)
					{
						resultSet1.close();
					}
					if (statement != null)
					{
						statement.close();
					}
				}
			}

		} finally
		{
			if (savepoint != null)
			{
				connection.releaseSavepoint(savepoint);
			}
			if (originalAutoCommit)
			{
				connection.setAutoCommit(true);
			}
		}
		return resultSet;
	}
}
